package net.ncraft.mc.easyframework.mvc;

import net.ncraft.mc.easyframework.FunctionRegister;
import net.ncraft.mc.easyframework.context.ApplicationContext;
import net.ncraft.mc.easyframework.context.anno.Autowired;
import net.ncraft.mc.easyframework.context.anno.Component;
import net.ncraft.mc.easyframework.mvc.anno.*;
import net.ncraft.mc.easyframework.mvc.command.CommandInterceptor;
import net.ncraft.mc.easyframework.mvc.command.CompletionInterceptor;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.TabCompleter;
import org.bukkit.command.TabExecutor;
import org.bukkit.event.Listener;
import org.bukkit.plugin.java.JavaPlugin;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.*;

@Component
public class EasyMVCAutoRegister implements FunctionRegister {

    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private JavaPlugin plugin;

    @Override
    public void register() {

        List<Class<?>> commandControllers = applicationContext.getComponents().stream().filter(c -> c.isAnnotationPresent(CommandController.class)).toList();
        List<Class<?>> completionControllers = applicationContext.getComponents().stream().filter(c -> c.isAnnotationPresent(CompletionController.class)).toList();
        List<Class<?>> eventControllers = applicationContext.getComponents().stream().filter(c -> c.isAnnotationPresent(EventController.class)).filter(Listener.class::isAssignableFrom).toList();

        {
            Map<String, List<Class<?>>> commands = new HashMap<>();
            Map<Class<?>, String> rootSigns = new HashMap<>();

            for (Class<?> commandController : commandControllers) {
                CommandController anno = commandController.getAnnotation(CommandController.class);
                String commandName = anno.value();
                String rootSign = anno.root();
                if (commands.containsKey(commandName)) {
                    commands.get(commandName).add(commandController);
                } else {
                    commands.put(commandName, new ArrayList<>());
                    commands.get(commandName).add(commandController);
                }
                rootSigns.put(commandController, rootSign);
            }

            for (String commandName : commands.keySet()) {
                List<Class<?>> commandControllerList = commands.get(commandName);
                Map<Class<?>, List<Method>> methods = new HashMap<>();

                for (Class<?> commandController : commandControllerList) {
                    List<Method> ms = Arrays.stream(commandController.getMethods()).filter(m -> m.isAnnotationPresent(Command.class))
                            .sorted(Comparator.comparingInt(m -> m.getAnnotation(Command.class).priority()))
                            .toList();
                    methods.put(commandController, ms);
                }

                Map<Class<?>, String> rootSign = new HashMap<>();

                for (Class<?> commandController : commandControllerList) {
                    rootSign.put(commandController, rootSigns.get(commandController));
                }

                CommandExecutor commandExecutor = (CommandExecutor) Proxy.newProxyInstance(plugin.getClass().getClassLoader(), new Class[]{CommandExecutor.class}, new CommandInterceptor(applicationContext, methods, rootSigns));
                plugin.getCommand(commandName).setExecutor(commandExecutor);
            }
        }

        {
            Map<String, List<Class<?>>> commands = new HashMap<>();
            Map<Class<?>, String> rootSigns = new HashMap<>();

            for (Class<?> completionController : completionControllers) {
                CompletionController anno = completionController.getAnnotation(CompletionController.class);
                String commandName = anno.value();
                String rootSign = anno.root();
                if (commands.containsKey(commandName)) {
                    commands.get(commandName).add(completionController);
                } else {
                    commands.put(commandName, new ArrayList<>());
                    commands.get(commandName).add(completionController);
                }
                rootSigns.put(completionController, rootSign);
            }

            for (String commandName : commands.keySet()) {
                List<Class<?>> completionControllerList = commands.get(commandName);
                Map<Class<?>, List<Method>> methods = new HashMap<>();

                for (Class<?> completionController : completionControllerList) {
                    List<Method> ms = Arrays.stream(completionController.getMethods()).filter(m -> m.isAnnotationPresent(Completion.class)).toList();
                    methods.put(completionController, ms);
                }

                Map<Class<?>, String> rootSign = new HashMap<>();

                for (Class<?> completionController : completionControllerList) {
                    rootSign.put(completionController, rootSigns.get(completionController));
                }

                TabExecutor tabExecutor = (TabExecutor) Proxy.newProxyInstance(plugin.getClass().getClassLoader(), new Class[]{TabExecutor.class}, new CompletionInterceptor(applicationContext, methods, rootSigns));
                plugin.getCommand(commandName).setTabCompleter(tabExecutor);
            }
        }
        {
            for (Class<?> eventController : eventControllers) {
                plugin.getServer().getPluginManager().registerEvents((Listener) applicationContext.getBean(eventController), plugin);
            }
        }

    }

}
